<?php

namespace App\Ma\Entities;

/**
 * 矩阵
 * Class Matrix
 * @package App\Ma\Entities
 */
class Matrix
{

    const TYPE_WAN = 0;
    const TYPE_TONG = 1;
    const TYPE_TIAO = 2;
    const TYPE_ZI = 3;
    const TYPE_SAN = 4;
    const TYPE_GUI = 5;
    const Types = ["万", "筒", "条"];
    const ZI_PAI = ['东', '南', '西', '北'];
    const SAN_YUAN = ['中', '发', '白'];
    const Matrix = [
        [0, 0, 0, 0, 0, 0, 0, 0, 0], // 0--万
        [0, 0, 0, 0, 0, 0, 0, 0, 0], // 1--筒
        [0, 0, 0, 0, 0, 0, 0, 0, 0], // 2--条
        [0, 0, 0, 0], // 3--字牌--东南西北
        [0, 0, 0], // 4--三元（箭牌）-- 中发白
        [0], // 5--癞子数量
    ];

    /**
     * 用户手牌
     * @var array
     */
    public $cards = [];

    /**
     * 用户手牌矩阵
     * @var array
     */
    public $cardMatrix = [];

    /**
     * 对子列表
     * @var array
     */
    public $dui = [];

    /**
     * 克子列表
     * @var array
     */
    public $ke = [];


    /**
     * 暗杠列表
     * @var array
     */
    public $anGang = [];

    /**
     * 手牌去除将眼后的列表
     * @var array
     */
    public $notEyeCardMatrix = [];


    /**
     * 是否十三幺
     * @var bool
     */
    public $is13 = false;


    /**
     * 组成7对需要的鬼牌数量
     * @var int
     */
    public $canUseLaiZiCount = 14;

    /**
     * 需要组成7大对需要的鬼牌数量
     * @var int
     */
    public $canUseLaiZiKangCount = 14;


    /**
     * 碰
     * @var array
     */
    public $pengList = [];


    /**
     * 暗杠
     * @var array
     */
    public $anGangList = [];

    /**
     * 明杠
     * @var array
     */
    public $mGangList = [];


    public function __construct(array $cards)
    {
        $this->cards = $cards;
        $this->cardMatrix = $this->cardListConvertToMatrix($cards);
        // 对子处理
        $this->twoCardsCounts();
    }


    /**
     * 获取鬼牌数量
     * @return int
     */
    public function getGuiCount(): int
    {
        return $this->cardMatrix[self::TYPE_GUI][0];
    }

    /**
     * 检查胡牌
     * @param int $huType
     * @return bool
     */
    public function checkHu(int &$huType = 0): bool
    {
        if ($this->is13) {
            // 十三幺胡牌
            return true;
        }
        // 检测7对
        $laiZiCount = $this->getGuiCount();
        if (count($this->dui) === 7) { //七小对胡牌
            if (count($this->anGang) > 0) { // 七大对胡牌

            }
            return true;
        } elseif ($this->canUseLaiZiKangCount <= $laiZiCount) { //(癞子)七大对胡牌
            return true;
        } elseif ($this->canUseLaiZiCount <= $laiZiCount) { //(癞子)七小对胡牌
            return true;
        }

        // 检测 $notEyeCardMatrix 胡牌
        if ($laiZiCount > 0) {
            foreach ($this->notEyeCardMatrix as $item) {
                $res = $this->checkLaiZiHu($item, $laiZiCount);
                if (true === $res) {
                    return true;
                }
            }
        } else {
            foreach ($this->notEyeCardMatrix as $item) {
                $res = MatrixUtils::checkMatrixAllElemEqualZero($item); // 矩阵统计值是否全部为0;
                if (true === $res) {
                    return true;
                }
            }
        }
        return false;
    }


    /**
     * 判断牌值再该手牌中是否可杠
     * @param int $value
     * @return bool
     */
    public function canGang(int $value): bool
    {
        return in_array($value, $this->ke);
    }

    /**
     * 判断牌值再该手牌中是否可碰
     * @param int $value
     * @return bool
     */
    public function canPeng(int $value): bool
    {
        $type = MatrixUtils::getValueIndex($value, 2);
        $val = MatrixUtils::getValueIndex($value, 3);
        return $this->cardMatrix[$type][$val] >= 2;
    }

    /**
     * 输出手牌
     * @return string
     */
    public function dump()
    {
        return MatrixUtils::matrixConvertToInfo($this->cardMatrix);
    }

    /**
     * 杠牌（机器人检测杠后权重使用）
     * @param int $value
     * @param bool $isAnGang
     * @return Matrix|false
     */
    public function gang(int $value, bool $isAnGang = false)
    {
        if (!$this->canGang($value)) {
            return false;
        }
        $type = MatrixUtils::getValueIndex($value, 2);
        $val = MatrixUtils::getValueIndex($value, 3);
        $this->cardMatrix[$type][$val] -= 2;
        $count = 0;
        $_cards = $this->cards;
        $cards = [];
        foreach ($_cards as $key => $card) {
            if ($count < 3 && $value === $card) {
                $count++;
                continue;
            }
            $cards[] = $card;
        }
        $this->cards = $cards;
        $this->twoCardsCounts();
        // 杠后无法实现七对胡牌
        $this->canUseLaiZiCount = 14;
        $this->canUseLaiZiKangCount = 14;
        if ($isAnGang) {
            array_push($this->mGangList, $value);
        } else {
            array_push($this->anGangList, $value);
        }

        return $this;
    }

    /**
     * 碰牌（机器人检测杠后权重使用）
     * @param int $value
     * @return Matrix|false
     */
    public function peng(int $value)
    {
        if (!$this->canPeng($value)) {
            return false;
        }
        $type = MatrixUtils::getValueIndex($value, 2);
        $val = MatrixUtils::getValueIndex($value, 3);
        $this->cardMatrix[$type][$val] -= 2;
        $count = 0;
        $_cards = $this->cards;
        $cards = [];
        foreach ($_cards as $key => $card) {
            if ($count < 2 && $value === $card) {
                $count++;
                continue;
            }
            $cards[] = $card;
        }
        $this->cards = $cards;
        // 碰后无法实现七对胡牌
        $this->canUseLaiZiCount = 14;
        $this->canUseLaiZiKangCount = 14;
        array_push($this->pengList, $value);
        return $this;
    }

    /**
     * 手牌转换为矩阵
     * @param array $cardList
     * @param bool $is13 // 是否十三幺牌型
     * @return array
     */
    protected function cardListConvertToMatrix(array $cardList, &$is13 = true): array
    {
        $matrix = self::Matrix;
        if (count($cardList) < 14) { // 非门清
            $is13 = false;
        }
        $isSameCount = 0;
        foreach ($cardList as $card) {
            $isLaiZi = ($card >> 8) & 0x0f; // 是否鬼牌
            $type = $isLaiZi ? self::TYPE_GUI : ($card >> 4) & 0x0f;
            $value = $card & 0x0F;
            $key = $isLaiZi ? 0 : $value - 1;
            $matrix[$type][$key]++;

            if ($is13 && $type <= self::TYPE_SAN) { // 检测十三幺牌型
                if ($matrix[$type][$key] > 1) {
                    $isSameCount++;
                }
                if ($isSameCount >= 2) { //有至少两队或者一个刻子时
                    $is13 = false;
                } else if ($type < self::TYPE_ZI && ($value > 1 && $value < 9)) { // 存在非1~9的花牌时
                    $is13 = false;
                }

            }
        }
        return $matrix;
    }

    /**
     * 对子处理
     * @return void
     */
    protected function twoCardsCounts()
    {
        $matrix = $this->cardMatrix;
        $dui = []; //对子列表
        $ke = []; //克子列表
        $anGang = []; //暗杠列表
        $tempMatrix = []; //无将眼列表
        foreach ($matrix as $type => $list) {
            foreach ($list as $key => $count) {
                if ($count >= 2) {
                    $temp = $matrix;
                    $temp[$type][$key] -= 2;
                    $tempMatrix[] = $temp;
                    unset($temp);

                    $card = MatrixUtils::generateHexValue($type, $key + 1);
                    switch ($count) {
                        case 2:
                            $dui[] = $card;
                            break;
                        case 3:
                            $ke[] = $card;
                            break;
                        case 4:
                            $anGang[] = $card;
                            break;
                    }
                }
            }
            if ($type >= self::TYPE_SAN) {
                // 后续的鬼牌
                break;
            }
        }
        // 组成7对需要的鬼牌数量
        $canUseLaiZiCount = (14 - count($dui) * 2) / 2;
        $this->canUseLaiZiCount = $canUseLaiZiCount;
        // 需要组成7大对时需要的鬼牌数量
        $this->canUseLaiZiKangCount = (count($ke) > 0 || count($anGang) > 0) ? $canUseLaiZiCount : $canUseLaiZiCount + 1;


        $this->dui = $dui;
        $this->ke = $ke;
        $this->anGang = $anGang;
        $this->notEyeCardMatrix = empty($dui) ? [$matrix] : $tempMatrix;
    }


    /**
     * 有鬼玩法检测是否胡牌
     * @param array $matrix
     * @param int $laiZiCount
     * @return bool
     */
    protected function checkLaiZiHu(array $matrix, int $laiZiCount): bool
    {
        foreach ($matrix as $type => $list) {
            foreach ($list as $key => $count) {
                if ($matrix[$type][$key] > 0) {
                    $temp = $matrix;
                    $needLaiZiCount = $temp[$type][$key] % 2;
                    $temp[$type][$key] = 0;
                    $needLaiZiCount = $this->checkNeedLaiZiCount($temp, $needLaiZiCount);
                    if ($needLaiZiCount <= $laiZiCount) {
                        return true;
                    }
                }
            }
            if (empty($this->dui)) {
                $needLaiZiCount = $this->checkNeedLaiZiCount($matrix, 2);
                if ($needLaiZiCount <= $laiZiCount) {
                    return true;
                }
            }
            if ($type >= self::TYPE_SAN) {
                // 后续的鬼牌
                break;
            }
        }
        return false;
    }


    /**
     * 检测胡牌需要的癞子数
     * @param array $matrix
     * @param int $needLaiZiCount
     * @return int
     */
    protected function checkNeedLaiZiCount(array $matrix, int $needLaiZiCount): int
    {
        $minLaiZiCount = $needLaiZiCount;
        foreach ($matrix as $type => $list) {
            foreach ($matrix[$type] as $key => $count) {
                $count = $matrix[$type][$key]; //解决$matrix[$type][$value + 1]等造成的影响
                while ($count > 0) {
                    // 可构建顺子的花牌
                    if ($type < 3 && $key < 7) {
                        if ($matrix[$type][$key + 1] > 0) {
                            $matrix[$type][$key]--;
                            $matrix[$type][$key + 1]--;
                            $minLaiZiCount++;
                            $count--;
                            continue;
                        }
                        if ($matrix[$type][$key + 2] > 0) {
                            $matrix[$type][$key]--;
                            $matrix[$type][$key + 2]--;
                            $minLaiZiCount++;
                            $count--;
                            continue;
                        }

                    }
                    // 构建克子
                    if ($matrix[$type][$key] == 1) {
                        $matrix[$type][$key]--;
                        $minLaiZiCount += 2;
                        $count--;
                        continue;
                    }
                    if ($matrix[$type][$key] == 2) {
                        $matrix[$type][$key] -= 2;
                        $minLaiZiCount++;
                        $count -= 2;
                        continue;
                    }
                    $count--;
                }

            }
            if ($type >= self::TYPE_SAN) {
                // 后续的鬼牌
                break;
            }
        }
        return $minLaiZiCount;
    }
}